package com.modificial.swagger;

import io.swagger.annotations.SwaggerDefinition;
import io.swagger.models.Swagger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.DocumentationCache;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper;
import springfox.documentation.swagger2.web.Swagger2Controller;

import java.util.ArrayList;
import java.util.List;

/**
 * @author modificial
 * @date 2018/7/31
 * @company codingApi
 * @description Swagger-UI自动配置类
 */
@Configuration
@EnableSwagger2
@ConditionalOnClass({DocumentationType.class, Swagger.class, Swagger2Controller.class, SwaggerDefinition.class})
@EnableConfigurationProperties({SwaggerProperties.class, ParameterProperties.class})
@ConditionalOnWebApplication
public class SwaggerAutoConfigration {

    private static final Logger LOGGER = LoggerFactory.getLogger(SwaggerAutoConfigration.class);

    @Autowired
    private SwaggerProperties swaggerProperties;

    @Autowired
    private ParameterProperties parameterProperties;

    @Bean
    @ConditionalOnMissingBean(Contact.class)
    public Contact contact() {
        SwaggerProperties.Contact contact = swaggerProperties.getContact();
        if (contact == null) {
            return new Contact("modificial", "#", "XXX@163.com");
        }
        return new Contact(contact.getName(), contact.getUrl(), contact.getEmail());
    }

    @Bean
    @ConditionalOnMissingBean(ApiInfo.class)
    public ApiInfo apiInfo(Contact contact) {
        return new ApiInfoBuilder().title(swaggerProperties.getTitle()).description(swaggerProperties.getDescription())
                .termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl()).contact(contact)
                .version(swaggerProperties.getVersion()).build();
    }

    @Bean
    @ConditionalOnMissingBean(Docket.class)
    @ConditionalOnProperty(value = "spring.swagger.scan-package")
    public Docket createRestApi(Contact contact) {
        LOGGER.info("Swagger scan Package: " + swaggerProperties.getScanPackage());
        Docket docket = new Docket(DocumentationType.SWAGGER_2).useDefaultResponseMessages(false).apiInfo(apiInfo(contact))
                .host(swaggerProperties.getHost());
        //若启用全局参数
        if (swaggerProperties.isUseGlobalOperationParameters()) {
            docket.globalOperationParameters(globalParameterBuild());
        }
        docket.select().apis(RequestHandlerSelectors.basePackage(swaggerProperties.getScanPackage()))
                .paths(PathSelectors.any()).build();
        return docket;
    }

    @Bean
    @ConditionalOnBean({DocumentationCache.class, ServiceModelToSwagger2Mapper.class})
    public Swagger2Facade swagger2Facade(DocumentationCache documentationCache, ServiceModelToSwagger2Mapper mapper) {
        return new Swagger2Facade(documentationCache, mapper);
    }

    /**
     * 构建全局参数
     * @return
     */
    private List<Parameter> globalParameterBuild() {
        Assert.notNull(parameterProperties.getName(),"The parameter name can't be null");
        Assert.notNull(parameterProperties.getModelRef(),"The moduleRef can't be null");
        Assert.notNull(parameterProperties.getParameterType(),"The parameter type can't be null");
        List<Parameter> pars = new ArrayList<Parameter>();
        ParameterBuilder parameterBuilder = new ParameterBuilder();
        Parameter parameter = parameterBuilder.name(parameterProperties.getName()).description(parameterProperties.getDescription())
                .modelRef(new ModelRef(parameterProperties.getModelRef()))
                .parameterType(parameterProperties.getParameterType())
                .required(parameterProperties.isRequired())
                .build();
        pars.add(parameter);
        return pars;
    }
}

